/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
/*
 * @Author: LVGRAPE
 * @LastEditors: LVGRAPE
 */
#include "drv_pin.h"
#include "drv_soft_i2c.h"

#define DRV_DEBUG
#define LOG_LVL DBG_LOG
#define LOG_TAG "drv.soft_i2c"
#include <rtdbg.h>

#define soft_i2c_delay() i2c_delay(self)

soft_i2c_t i2c1;

static void i2c_delay(soft_i2c_t *self)
{
    __IO rt_uint32_t ticks;
    __IO rt_uint32_t told, tnow, tcnt = 0;
    __IO rt_uint32_t reload = SysTick->LOAD; // 199999

    // ticks = us * reload / (1000000 / RT_TICK_PER_SECOND) / 2;//199
    ticks = self->delay * 10;
    told = SysTick->VAL;
    while (1)
    {
        tnow = SysTick->VAL;
        if (tnow != told)
        {
            if (tnow < told)
            {
                tcnt += told - tnow;
            }
            else
            {
                tcnt += reload - tnow + told;
            }
            told = tnow;
            if (tcnt >= ticks)
            {
                break;
            }
        }
    }
}
// #define soft_i2c_delay() __NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();__NOP();
/**
 * @brief 初始化I2C
 *
 * @param self
 * @param scl_port
 * @param scl_pin
 * @param sda_port
 * @param sda_pin
 * @param freq
 * @return int8_t
 */
int8_t soft_i2c_init(soft_i2c_t *self, gpio_type *scl_port, uint16_t scl_pin, gpio_type *sda_port, uint16_t sda_pin, uint32_t freq)
{
    self->scl_port = scl_port;
    self->sda_port = sda_port;
    self->scl_pin = scl_pin;
    self->sda_pin = sda_pin;
    self->freq = freq;
    self->delay = 1000000 / self->freq;

    crm_periph_clock_enable(CRM_GPIOF_PERIPH_CLOCK, TRUE);

    gpio_init_type gpio_init_struct;
    gpio_default_para_init(&gpio_init_struct);
    gpio_init_struct.gpio_pins = self->sda_pin;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(self->sda_port, &gpio_init_struct);

    gpio_init_struct.gpio_pins = self->scl_pin;
    gpio_init_struct.gpio_mode = GPIO_MODE_OUTPUT;
    gpio_init_struct.gpio_out_type = GPIO_OUTPUT_OPEN_DRAIN;
    gpio_init_struct.gpio_drive_strength = GPIO_DRIVE_STRENGTH_MAXIMUM;
    gpio_init_struct.gpio_pull = GPIO_PULL_UP;
    gpio_init(self->scl_port, &gpio_init_struct);

    if (rt_mutex_init(&self->lock, "i2c_lock", RT_IPC_FLAG_FIFO) != RT_EOK)
    {
        return -1;
    }

    IO_SET(self->sda_port, self->sda_pin);
    IO_SET(self->scl_port, self->scl_pin);

    return 0;
}

#define I2C_SDA_1() IO_SET(self->sda_port, self->sda_pin)
#define I2C_SDA_0() IO_RESET(self->sda_port, self->sda_pin)
#define I2C_SCL_1() IO_SET(self->scl_port, self->scl_pin)
#define I2C_SCL_0() IO_RESET(self->scl_port, self->scl_pin)
#define I2C_SDA_READ() IO_READ(self->sda_port, self->sda_pin)
#define I2C_SCL_READ() IO_READ(self->scl_port, self->scl_pin)

int8_t soft_i2c_io_check(soft_i2c_t *self)
{
    int8_t ret = 0;
    if (I2C_SCL_READ() == 0)
    {
        ret |= 0x01;
        LOG_E("scl can't be high ! port: 0x%08X pin: 0x%08X", self->scl_port, self->scl_pin);
    }
    else
    {
        I2C_SCL_0();
        soft_i2c_delay();
        if (I2C_SCL_READ())
        {
            ret |= 0x02;
            LOG_E("scl can't be low ! port: 0x%08X pin: 0x%08X", self->scl_port, self->scl_pin);
        }
        // I2C_SCL_1();
    }
    if (I2C_SDA_READ() == 0)
    {
        ret |= 0x04;
        LOG_E("sda can't be high ! port: 0x%08X pin: 0x%08X", self->sda_port, self->sda_pin);
    }
    else
    {
        I2C_SDA_0();
        soft_i2c_delay();
        if (I2C_SDA_READ())
        {
            ret |= 0x08;
            LOG_E("sda can't be low ! port: 0x%08X pin: 0x%08X", self->sda_port, self->sda_pin);
        }
        // I2C_SDA_1();
    }
    I2C_SDA_1();
    soft_i2c_delay();
    I2C_SCL_1();
    soft_i2c_delay();
    return ret;
}

void soft_i2c_start(soft_i2c_t *self)
{
    /* 当SCL高电平时，SDA出现一个下跳沿表示I2C总线启动信号 */
    I2C_SDA_1();
    soft_i2c_delay();
    I2C_SCL_1();
    soft_i2c_delay();
    I2C_SDA_0();
    soft_i2c_delay();
    // I2C_SCL_0();
    // soft_i2c_delay();
}
void soft_i2c_stop(soft_i2c_t *self)
{
    /* 当SCL高电平时，SDA出现一个上跳沿表示I2C总线停止信号 */
    I2C_SDA_0();
    soft_i2c_delay();
    I2C_SCL_1();
    soft_i2c_delay();
    I2C_SDA_1();
    soft_i2c_delay();
}
/**
 * @brief 发送一个字节
 *
 * @param self
 * @param byte
 * @return int8_t 1: success, 0: fail
 */
int8_t soft_i2c_send_byte(soft_i2c_t *self, uint8_t byte)
{
    uint8_t i;
    /* 先发送字节的高位bit7 */
    for (i = 0; i < 8; i++)
    {
        I2C_SCL_0(); // change data at low

        // soft_i2c_delay();
        if (byte & 0x80)
        {
            I2C_SDA_1();
        }
        else
        {
            I2C_SDA_0();
        }
        soft_i2c_delay();
        I2C_SCL_1(); // hold data at high
        soft_i2c_delay();
        byte <<= 1; /* 左移一个bit */
    }
    I2C_SCL_0(); // change data at low
    // soft_i2c_delay();
    I2C_SDA_1(); // 释放总线
    return soft_i2c_wait_ack(self);
}
uint8_t soft_i2c_recv_byte(soft_i2c_t *self, int8_t ack)
{
    uint8_t i;
    uint8_t value=0;
    /* 读到第1个bit为数据的bit7 */
    I2C_SDA_1(); // 释放总线
    // soft_i2c_delay();
    for (i = 0; i < 8; i++)
    {
        value <<= 1;
        I2C_SCL_1();
        // soft_i2c_delay();
        __NOP();
        __NOP();
        __NOP();
        __NOP();
        if (I2C_SDA_READ())
        {
            value |= 0x01;
        }
        I2C_SCL_0();
        soft_i2c_delay();
    }
    soft_i2c_send_ack_or_nack(self, ack);
    return value;
}
/**
 * @brief 等待应答信号
 *
 * @param self
 * @return rt_bool_t 1: ack, 0: nack
 */
rt_bool_t soft_i2c_wait_ack(soft_i2c_t *self)
{
    rt_bool_t ack;
    I2C_SDA_1(); /* CPU释放SDA总线 */
    soft_i2c_delay();
    I2C_SCL_1(); /* CPU驱动SCL = 1, 此时器件会返回ACK应答 */
    soft_i2c_delay();
    ack = !I2C_SDA_READ(); /* CPU读取SDA口线状态 */
    I2C_SCL_0();
    return ack;
}
int8_t soft_i2c_send_ack_or_nack(soft_i2c_t *self, int8_t ack)
{

    if (ack)
    {
        I2C_SDA_0();
    }
    soft_i2c_delay();
    I2C_SCL_1();
    soft_i2c_delay();
    I2C_SCL_0();
    I2C_SDA_1();
    return 0;
}
/**
 * @brief 发送从机地址
 *
 * @param self
 * @param slavAddr
 * @param retries
 * @return int8_t 1: success, 0: fail
 */
static int8_t soft_i2c_send_address(soft_i2c_t *self, uint8_t slavAddr, uint8_t retries)
{
    int8_t i;
    uint8_t ret = 0;
    for (i = 0; i < retries; i++)
    {
        // soft_i2c_start(self);
        ret = soft_i2c_send_byte(self, slavAddr);
        if (ret == 1 || i == retries)
        {
            break;
        }
        soft_i2c_stop(self);
        soft_i2c_delay();
        soft_i2c_delay();
        soft_i2c_delay();
        soft_i2c_delay();
        soft_i2c_delay();
        // soft_i2c_start(self);
    }
    return ret;
}
int32_t soft_i2c_write(soft_i2c_t *self, uint8_t slavAddr, uint8_t reg, uint8_t *data, uint16_t len)
{
    int32_t ret = 0;
    int32_t i;
    uint8_t addr = slavAddr << 1;
    rt_mutex_take(&self->lock, RT_WAITING_FOREVER);

    // ret = soft_i2c_io_check(self);
    // if (ret != 0)
    // {
    //     // NOTE i2c bus error
    //     ret = -1;
    //     goto wout;
    // }

    soft_i2c_start(self);
    ret = soft_i2c_send_address(self, addr, 3);
    if (ret != 1)
    {
        // NOTE send address fail
        ret = -2;
        goto wout;
    }
    ret = soft_i2c_send_byte(self, reg);
    if (ret != 1)
    {
        // NOTE send register fail
        ret = -3;
        goto wout;
    }
    for (i = 0; i < len; i++)
    {
        ret = soft_i2c_send_byte(self, *data);
        if (ret > 1)
        {
            data++;
        }
        else
        {
            // NOTE send data fail at index i
            ret = i;
            goto wout;
        }
    }
wout:
    soft_i2c_stop(self);
    rt_mutex_release(&self->lock);
    return ret;
}
int32_t soft_i2c_read(soft_i2c_t *self, uint8_t slavAddr, uint8_t reg, uint8_t *data, uint16_t len)
{
    int32_t ret = 0;
    int32_t i;
    uint8_t addr = slavAddr << 1;
    rt_mutex_take(&self->lock, RT_WAITING_FOREVER);

    // ret = soft_i2c_io_check(self);
    // if (ret != 0)
    // {
    //     // NOTE i2c bus error
    //     ret = -1;
    //     goto rout;
    // }

    soft_i2c_start(self);
    ret = soft_i2c_send_address(self, addr, 3);
    if (ret != 1)
    {
        // NOTE send address fail
        ret = -2;
        goto rout;
    }
    ret = soft_i2c_send_byte(self, reg);
    if (ret != 1)
    {
        // NOTE send register fail
        ret = -3;
        goto rout;
    }
    soft_i2c_start(self);
    ret = soft_i2c_send_address(self, addr | 0x01, 1);
    if (ret != 1)
    {
        // NOTE send address fail
        ret = -4;
        goto rout;
    }
    for (i = 0; i < (len - 1); i++)
    {
        *data = soft_i2c_recv_byte(self, 1);
        data++;
    }
    *data = soft_i2c_recv_byte(self, 0);
    ret = len;

rout:
    soft_i2c_stop(self);
    rt_mutex_release(&self->lock);
    return ret;
}

void soft_i2c1_init(void)
{
    soft_i2c_init(&i2c1, GPIOF, GPIO_PINS_6, GPIOF, GPIO_PINS_7, 1000 * 1000);
}
/**
 * @brief
 *
 * @param slavAddr
 * @param reg
 * @param data
 * @param len
 * @return int32_t <0 error, >=0 success bytes
 */
int32_t soft_i2c1_read(uint8_t slavAddr, uint8_t reg, uint8_t *data, uint16_t len)
{
    return soft_i2c_read(&i2c1, slavAddr, reg, data, len);
}
/**
 * @brief
 *
 * @param slavAddr
 * @param reg
 * @param data
 * @param len
 * @return int32_t <0 error, >=0 success bytes
 */
int32_t soft_i2c1_write(uint8_t slavAddr, uint8_t reg, uint8_t *data, uint16_t len)
{
    return soft_i2c_write(&i2c1, slavAddr, reg, data, len);
}
uint8_t soft_i2c1_scan(void)
{
    uint8_t addr = 0;
    uint8_t devCnt = 0;
    rt_kprintf("\n ---- testing i2c io ...\n");
    int ret = soft_i2c_io_check(&i2c1);
    if (ret != 0)
    {
        rt_kprintf("\n---- i2c bus error ---- %02X\n", ret);
    }
    else
    {
        rt_kprintf("\n---- i2c io ok ----\n");
    }
    rt_thread_mdelay(1);

    rt_kprintf("\n---- scaning i2c device ----\n");
    for (addr = 1; addr < 128; addr++)
    {
        if (soft_i2c_send_address(&i2c1, addr << 1, 1) == 1)
        {
            devCnt++;
            rt_kprintf("---- get dev: 0x%02X ----\n", addr);
        }
        rt_thread_mdelay(5);
    }
    if (devCnt == 0)
    {
        rt_kprintf("---- no dev found ----\n");
    }

    return devCnt;
}
MSH_CMD_EXPORT(soft_i2c1_scan, "scan i2c device");
